package com.shuwen.gcdj.service.role;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.shuwen.gcdj.bean.role.request.QueryPageReq;
import com.shuwen.gcdj.common.enums.LogModuleEnum;
import com.shuwen.gcdj.common.enums.LogTypeEnum;
import com.shuwen.gcdj.common.exception.ApiException;
import com.shuwen.gcdj.bean.AddResp;
import com.shuwen.gcdj.bean.role.request.AddRoleReq;
import com.shuwen.gcdj.bean.role.request.GetAllRoleReq;
import com.shuwen.gcdj.bean.role.request.UpdateRoleReq;
import com.shuwen.gcdj.bean.role.response.RoleItemResp;
import com.shuwen.gcdj.common.constant.Constants;
import com.shuwen.gcdj.common.constant.RolePowerCst;
import com.shuwen.gcdj.common.enums.ErrorCode;
import com.shuwen.gcdj.common.sysLog.AddSysLogUtil;
import com.shuwen.gcdj.common.util.account.AccountUtil;
import com.shuwen.gcdj.dto.account.InitUserDto;
import com.shuwen.gcdj.dto.role.CheckAddRespDto;
import com.shuwen.gcdj.dto.role.SearchRolePowerDto;
import com.shuwen.gcdj.entity.AccountRole;
import com.shuwen.gcdj.entity.Role;
import com.shuwen.gcdj.exception.IErrorCodeFactory;
import com.shuwen.gcdj.mapper.mysql.AccountRoleMapper;
import com.shuwen.gcdj.mapper.mysql.RoleMapper;
import com.shuwen.gcdj.service.BaseService;
import com.shuwen.gcdj.service.redis.RedisService;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
@Slf4j
public class RoleService extends BaseService<RoleMapper, Role> {

    private final RoleMapper roleMapper;
    private final AccountRoleMapper accountRoleMapper;
    private final AddSysLogUtil addSysLogUtil;

    private final RolePowerService rolePowerService;
    private final RedisService redisService;

    public List<Role> getMultiByRoleIds(List<String> roleIds) {
        if (roleIds == null || roleIds.isEmpty()) {
            return new ArrayList<>();
        }

        return roleMapper.selectBatchIds(roleIds);
    }

    /**
     * 添加角色
     *
     * @param req         AddRoleReq
     * @param initUserDto InitUserDto
     * @return AddResp
     */
    public AddResp createRole(AddRoleReq req, InitUserDto initUserDto) {
        // check add
//      判断是不是同名已删除角色，是 修改id,和is_delete字段
//      不是就新增
        CheckAddRespDto check = checkAddReturn(req);
        AddResp resp = new AddResp();

        // add action
        Role r = new Role();
        r.setSort(1);

        if (initUserDto != null && initUserDto.getUserId() != null) {
            r.setUpdateId(initUserDto.getUserId());
        }
        if (StringUtils.isEmpty(req.getParentId())) {
            r.setParentId("0");
        }else{
            r.setParentId(req.getParentId());
        }

        if (req.getSort() != null) {
            r.setSort(req.getSort());
        }
        r.setIsEnable(req.getIsEnable());
        r.setName(req.getName());

        Optional.ofNullable(req.getCode()).ifPresent(r::setCode);
        r.setLevel(check.getAddLevel());
        // 编辑已有同名角色
        if (check.getAddType().equals(Constants.ADD_TYPE_WITH_UPDATE)) {
            r.setId(check.getCurrentRole().getId());
            r.setIsDelete(Constants.IS_DELETE_NORMAL);
            r.setUpdateTime(null);
            updateById(r);
            addSysLogUtil.addLog(initUserDto, LogTypeEnum.LOG_TYPE_UPDATE.getTypeNum(), LogModuleEnum.LOG_MODULE_ROLE.getModuleNum(),"编辑已有同名角色"+r.getCode(),req);
            resp.setId(check.getCurrentRole().getId());

            // 清空用户角色缓存 + 权限缓存
            redisService.deleteKeysWithPattern(AccountUtil.buildUserRolesKey("*"));
            redisService.deleteKeysWithPattern(AccountUtil.buildUserPowersKey("*"));

            return resp;
        }

        // 新增角色
        if (initUserDto != null) {
            Optional.ofNullable(initUserDto.getUserId()).ifPresent(r::setCreateId);
        }
        boolean addRes = save(r);
        if (addRes) {
            addSysLogUtil.addLog(initUserDto,LogTypeEnum.LOG_TYPE_ADD.getTypeNum(),LogModuleEnum.LOG_MODULE_ROLE.getModuleNum(),"添加了一个角色"+r.getCode(),req);
            resp.setId(r.getId());
            // 清空用户角色缓存 + 权限缓存
            redisService.deleteKeysWithPattern(AccountUtil.buildUserRolesKey("*"));
            redisService.deleteKeysWithPattern(AccountUtil.buildUserPowersKey("*"));
            return resp;
        }

        log.error("RoleService.createRoleAction.save({}) error", r);
        return null;
    }

    /**
     * 修改角色
     *
     * @param req         UpdateRoleReq
     * @param initUserDto InitUserDto
     */
    public void updateRole(UpdateRoleReq req, InitUserDto initUserDto) {
        if (req == null || req.getId() == null || req.getId().length() == 0) {
            throw new ApiException("角色ID不能为空");
        }

        Role role = getById(req.getId());
        if (role == null) {
            throw new ApiException("修改角色不存在");
        }
//        判断没删除的角色code是否存在
        LambdaQueryWrapper<Role> eq = new QueryWrapper<Role>().lambda().eq(Role::getId, req.getId()).eq(Role::getIsDelete,Constants.IS_DELETE_NORMAL);
        Role roleExist = roleMapper.selectOne(eq);
        if(roleExist != null && roleExist.getCode().equals(req.getCode())){
            throw new ApiException("角色code已存在");
        }
        if (isRoleDelete(Lists.newArrayList(req.getId()))) {
            throw new ApiException(IErrorCodeFactory.create(ErrorCode.INVALID_OP.getCode(), "锁定角色不允许修改"));
        }

        BeanUtils.copyProperties(req, role);
//        使用了自动填充时间，需要设置为null
        role.setUpdateTime(null);

        updateById(role);

        // 清空用户角色缓存 + 权限缓存
        redisService.deleteKeysWithPattern(AccountUtil.buildUserRolesKey("*"));
        redisService.deleteKeysWithPattern(AccountUtil.buildUserPowersKey("*"));
    }

    /**
     * 批量删除角色
     *
     * @param roleId      String
     * @param initUserDto InitUserDto
     */
    public void deleteRole(String roleId, InitUserDto initUserDto) {
        String[] split = roleId.split(",");
        //        日志使用codes
        List<String> codes = new ArrayList<>();
//        批量删除角色 ，及角色下面的子角色
        for (String s : split){
            if (isRoleDelete(Lists.newArrayList(s))) {
                log.error("deleteRole.isSupper(roleId = {}, initUserInfo = {})试图删除锁定角色，这是不可能让你得逞的～..～", roleId, initUserDto);
                throw new ApiException(IErrorCodeFactory.create(ErrorCode.INVALID_OP.getCode(), "锁定角色不允许删除"));
            }
            // search current & sub roles
            List<Role> roles = searchCurrentAndSubRoles(Lists.newArrayList(s));

            log.info("deleteRole.searchCurrentAndSubRoles() roles = {}", roles);
            if (!CollectionUtils.isEmpty(roles)) {
                List<String> roleIds = new ArrayList<>();
                for (Role role : roles) {
                    role.setIsDelete(Constants.IS_DELETE_YES);
                    role.setUpdateTime(null);
                    if (initUserDto != null && initUserDto.getUserId() != null) {
                        role.setUpdateId(initUserDto.getUserId());
                    }
                    codes.add(role.getCode());
                    roleIds.add(role.getId());
                }

                // 检验角色是否关联用户
                if (roleHasBindUser(roleIds)) {
                    throw new ApiException("已有账号绑定，不可删除");
                }

                // 删除角色
                updateBatchById(roles);
                // 删除角色与权限码绑定关系
                SearchRolePowerDto search = new SearchRolePowerDto();
                search.setRoleIds(roleIds);
                rolePowerService.deleteRolePower(search, initUserDto);

                // 清空用户角色缓存 + 权限缓存
                redisService.deleteKeysWithPattern(AccountUtil.buildUserRolesKey("*"));
                redisService.deleteKeysWithPattern(AccountUtil.buildUserPowersKey("*"));
            }
        }
        addSysLogUtil.addLog(initUserDto, LogTypeEnum.LOG_TYPE_DELETE.getTypeNum(), LogModuleEnum.LOG_MODULE_ROLE.getModuleNum(), "删除了角色"+codes.toString(),roleId);
    }


    /**
     * 获取当前角色 + 子集角色
     *
     * @param parentId List<String>
     * @return List<Role>
     */
    public List<Role> searchCurrentAndSubRoles(List<String> parentId) {
        List<Role> c = new ArrayList<>();
        if (parentId == null || parentId.isEmpty()) {
            return c;
        }

        QueryWrapper<Role> qw = new QueryWrapper<>();
        qw.in("id", parentId);
        qw.eq(Constants.FIELD_KEY_IS_DELETE, Constants.IS_DELETE_NORMAL);
        c = roleMapper.selectList(qw);
        if (c == null) {
            c = new ArrayList<>();
        }

        searchSubRoles(parentId, c);
        return c;
    }


    /**
     * 递归调用获取子集角色
     *
     * @param parentId List<String>
     */
    public void searchSubRoles(List<String> parentId, List<Role> c) {
        if (parentId == null || parentId.isEmpty()) {
            return;
        }

        QueryWrapper<Role> qw = new QueryWrapper<>();
        qw.in("parent_id", parentId);
        qw.eq(Constants.FIELD_KEY_IS_DELETE, Constants.IS_DELETE_NORMAL);
        List<Role> roles = roleMapper.selectList(qw);
        if (roles != null) {
            List<String> newParentId = new ArrayList<>();
            for (Role role : roles) {
                newParentId.add(role.getId());
            }
            c.addAll(roles);
            searchSubRoles(newParentId, c);
        }
    }

    private CheckAddRespDto checkAddReturn(AddRoleReq req) {
        if (req.getName() == null || req.getName().length() == 0) {
            throw new ApiException("角色名不能为空");
        }
        if (req.getCode() == null || req.getCode().length() == 0) {
            throw new ApiException("角色code不能为空");
        }

        CheckAddRespDto dto = new CheckAddRespDto();
        dto.setAddLevel(Constants.INIT_LEVEL);

        if (!StringUtils.isEmpty(req.getParentId())) {
            Role parent = getById(req.getParentId());
            Optional.ofNullable(parent).ifPresent(dto::setParentRole);
            if (parent != null && parent.getLevel() != null) {
                dto.setAddLevel(parent.getLevel() + 1);
            }
        }

        QueryWrapper<Role> q = new QueryWrapper<>();
        q.lambda().eq(Role::getCode,req.getCode());
        Role role = getOne(q);
        if (role != null) {
            if (role.getIsDelete().equals(Constants.IS_DELETE_NORMAL)) {
                throw new ApiException("同一层级角色code已存在");
            }

            dto.setCurrentRole(role);
            dto.setAddType(Constants.ADD_TYPE_WITH_UPDATE);
            return dto;
        }

        // 正常添加
        dto.setAddType(Constants.ADD_TYPE_NORMAL_ADD);
        return dto;
    }

    /**
     * 检查角色是否关联有效用户
     *
     * @param roleIds List<String>
     * @return boolean
     */
    public boolean roleHasBindUser(List<String> roleIds) {
        if (roleIds == null || roleIds.isEmpty()) {
            return false;
        }

        QueryWrapper<AccountRole> qw = new QueryWrapper<>();
        qw.in("role_id", roleIds);
        qw.eq(Constants.FIELD_KEY_IS_DELETE, Constants.IS_DELETE_NORMAL);
        qw.last("limit 1");
        AccountRole ar = accountRoleMapper.selectOne(qw);
        return ar != null && ar.getId() != null && !ar.getId().isEmpty();
    }

    /**
     * 分页获取所有角色列表(不返回超级管理员)
     *
     * @return RoleItemResp
     */
    public Page<RoleItemResp> getAllList(GetAllRoleReq req, QueryPageReq pages) {

        Page<RoleItemResp> page = new Page<>(pages.getPageNum(), pages.getPageSize(), true);
        IPage<RoleItemResp> allList = roleMapper.getAllList(page,req);
        List<RoleItemResp> res = allList.getRecords()
                .stream()
                .filter(r->!r.getCode().equals(RolePowerCst.ROLE_CODE_SUPER))
                .collect(Collectors.toList());
        Page<RoleItemResp> resPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        resPage.setRecords(res);

        return resPage;
    }

    /**
     * 通过用户id获取所有角色列表
     *
     * @return RoleItemResp
     */
    public List<RoleItemResp> getbyroleidAllList(GetAllRoleReq req,String id) {

        List<AccountRole> accountRole = accountRoleMapper.selectList(new QueryWrapper<AccountRole>().lambda().eq(AccountRole::getAccountId, id));
        if(CollectionUtils.isEmpty(accountRole)){
            return new ArrayList<>();
        }
        return roleMapper.getbyaccountid(req,accountRole);
    }

    /**
     * 检验角色是不是超级管理员角色
     *
     * @param roleIds List<String>
     * @return boolean
     */
    public boolean isSupper(List<String> roleIds) {
        if (roleIds == null || roleIds.isEmpty()) {
            return false;
        }

        List<Role> roles = roleMapper.selectBatchIds(roleIds);
        for (Role role : roles) {
            if (role.getCode() != null && !role.getCode().equals("") && role.getCode().equals(RolePowerCst.ROLE_CODE_SUPER)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 检验角色是不是可以删除，修改
     *
     * @param roleIds List<String>
     * @return boolean
     */
    public boolean isRoleDelete(List<String> roleIds) {
        if (roleIds == null || roleIds.isEmpty()) {
            return false;
        }

        List<Role> roles = roleMapper.selectBatchIds(roleIds);
        for (Role role : roles) {
            if (role.getIsLock() != null && !role.getIsLock().equals("") && role.getIsLock().equals(RolePowerCst.ROLE_ISLOCK)) {
                return true;
            }
        }

        return false;
    }

    public RoleItemResp getByroleId( GetAllRoleReq req ,String id){
        return roleMapper.getByroleId(req, id);
    }

    public List<RoleItemResp> getRoleList() {

        return roleMapper.getRoleList();
    }
}
